package pacBioSimple;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//import java.util.logging.Logger;

import tools.kmer.KmerSet_binary;
import tools.kmer.kmerFunctions;

public class MyUtil 
{
//	private static Logger log = Logger.getLogger(PacBioKmerAlgoMain.class.getName());
	private static Map<BitSet,String> bitToString=null;
	private static Map<String,BitSet> stringToBit=null;
	
	
	
	public static int checkKmerDistribution(String kmer, KmerSet_binary kmerDB)
	{
		//System.out.print(kmer+ " kmer check\n");
		if(kmer.equals(""))
		{
			return 0;
		}
		kmer=kmer.toUpperCase();
		String kmerToUse = kmerFunctions.kmerToUse(kmer);
		BitSet bS = kmerDB.getUtils().stringToBitSet(kmerToUse);
		//BitSet bS = ks.getUtils().stringToBitSet(kmerFunctions.kmerToUse(kmer.toString()));
		if(kmerDB.exists(kmerToUse))
		{
			return(kmerDB.getNumber(bS));
		}
		else
		{
			return 0;
		}
	}
	
	
	
	public static void logging(String message)
	{
//		if(log.getLevel()==Level.ALL)
//		{
//			System.out.println(message);
//			//log.log(Level.ALL,message);
//		}
		System.out.println(message);
	}
	
	public static String convertBitToDimer(BitSet bit, String dimer)
	{
		if(stringToBit==null && bitToString==null)
		{
			stringToBit=new HashMap<String, BitSet>();
			bitToString=new HashMap<BitSet, String>();
			MyUtil.initMap();
		}
		dimer=bitToString.get(bit);
		return dimer;
	}
	
	public static BitSet convertStringToBit(String dimer,BitSet bit)
	{
		if(stringToBit==null && bitToString==null)
		{
			stringToBit=new HashMap<String, BitSet>();
			bitToString=new HashMap<BitSet, String>();
			MyUtil.initMap();
		}
		bit=stringToBit.get(dimer);
		return bit;
	}

	public static<T,A> void copyToDoubleMap(Map<T,A>map,Map<T,HashMap<A,Integer>>map2)
	{
		for(Map.Entry<T, A> entry : map.entrySet())
		{
			addToDoubleMap(map2, entry.getKey(), entry.getValue());
		}
	}
	
	public static<T,A> void addToDoubleMap(Map<T,HashMap<A,Integer> > map, T key, A value)
	{
		if(!map.containsKey(key))
		{
			map.put(key, new HashMap<A, Integer>());
			map.get(key).put(value,1);
		}
		else
		{
			if(!map.get(key).containsKey(value))
			{
				map.get(key).put(value, 1);
			}
			else
			{
				map.get(key).put(value, map.get(key).get(value)+1);
			}
		}
	}
	
	
	public static<T,A> void addToMapNoCollision(Map<T,A> map, T key, A value)
	{
		if(!map.containsKey(key))
		{
			map.put(key, value);
		}
//		else
//		{
//			System.err.println("map collision appeared key value "+key+" "+value);
//		}
	}
	

	public static<T> int addToMap(Map<T,Integer> map, T key)
	{
		if(!map.containsKey(key))
		{
			map.put(key, 1);
			return 1;
		}
		else
		{
			map.put(key, map.get(key)+1);
			return map.get(key);
		}
//		else
//		{
//			System.err.println("map collision appeared key value "+key+" "+value);
//		}
	}
	
	public static BitSet createBitSetFromString( String s ) {
		  BitSet ret = new BitSet( s.length() );
		  for( int i = 0 ; i < s.length() ; ++i )
		  {
		    if(s.charAt(i) == '1') 
		    {
		    	ret.set( s.length() - 1 - i ) ;

		    }
		  }
		  return ret ;
		}

	private static void initMap()
	{
		BitSet bs=null;
		for(int i=0; i<20;++i)
		{
			if(i == 0)
			{
				bs = createBitSetFromString(Integer.toBinaryString(0));
				addToMapNoCollision(MyUtil.bitToString,bs, "AG");
				addToMapNoCollision(MyUtil.stringToBit,"AG",bs);
			}
			if(i == 1)
			{
				bs = createBitSetFromString(Integer.toBinaryString(1));
				addToMapNoCollision(MyUtil.bitToString,bs, "AG");
				addToMapNoCollision(MyUtil.stringToBit,"AG",bs);
			}
			else if(i==2)
			{
				bs = createBitSetFromString(Integer.toBinaryString(2));
				addToMapNoCollision(MyUtil.bitToString,bs, "AT");
				addToMapNoCollision(MyUtil.stringToBit,"AT",bs);
			}
			else if(i==3)
			{
				bs = createBitSetFromString(Integer.toBinaryString(3));
				addToMapNoCollision(MyUtil.bitToString,bs, "A-");
				addToMapNoCollision(MyUtil.stringToBit,"A-",bs);
			}
			else if(i==4)
			{
				bs = createBitSetFromString(Integer.toBinaryString(4));
				addToMapNoCollision(MyUtil.bitToString,bs, "CA");
				addToMapNoCollision(MyUtil.stringToBit,"CA",bs);
			}
			else if(i==5)
			{
				bs = createBitSetFromString(Integer.toBinaryString(5));
				addToMapNoCollision(MyUtil.bitToString,bs, "CG");
				addToMapNoCollision(MyUtil.stringToBit,"CG",bs);
			}
			else if(i==6)
			{
				bs = createBitSetFromString(Integer.toBinaryString(6));
				addToMapNoCollision(MyUtil.bitToString,bs, "CT");
				addToMapNoCollision(MyUtil.stringToBit,"CT",bs);
			}
			else if(i==7)
			{
				bs = createBitSetFromString(Integer.toBinaryString(7));
				addToMapNoCollision(MyUtil.bitToString,bs, "C-");
				addToMapNoCollision(MyUtil.stringToBit,"C-",bs);
			}
			else if(i==8)
			{
				bs = createBitSetFromString(Integer.toBinaryString(8));
				addToMapNoCollision(MyUtil.bitToString,bs, "GA");
				addToMapNoCollision(MyUtil.stringToBit,"GA",bs);
			}
			else if(i==9)
			{
				bs = createBitSetFromString(Integer.toBinaryString(9));
				addToMapNoCollision(MyUtil.bitToString,bs, "GC");
				addToMapNoCollision(MyUtil.stringToBit,"GC",bs);
			}
			else if(i==10)
			{
				bs = createBitSetFromString(Integer.toBinaryString(10));
				addToMapNoCollision(MyUtil.bitToString,bs, "GT");
				addToMapNoCollision(MyUtil.stringToBit,"GT",bs);
			}
			else if(i==11)
			{
				bs = createBitSetFromString(Integer.toBinaryString(11));
				addToMapNoCollision(MyUtil.bitToString,bs, "G-");
				addToMapNoCollision(MyUtil.stringToBit,"G-",bs);
			}
			else if(i==12)
			{
				bs = createBitSetFromString(Integer.toBinaryString(12));
				addToMapNoCollision(MyUtil.bitToString,bs, "TA");
				addToMapNoCollision(MyUtil.stringToBit,"TA",bs);
			}
			else if(i==13)
			{
				bs = createBitSetFromString(Integer.toBinaryString(13));
				addToMapNoCollision(MyUtil.bitToString,bs, "TC");
				addToMapNoCollision(MyUtil.stringToBit,"TC",bs);
			}
			else if(i==14)
			{
				bs = createBitSetFromString(Integer.toBinaryString(14));
				addToMapNoCollision(MyUtil.bitToString,bs, "TG");
				addToMapNoCollision(MyUtil.stringToBit,"TG",bs);
			}
			else if(i==15)
			{
				bs = createBitSetFromString(Integer.toBinaryString(15));
				addToMapNoCollision(MyUtil.bitToString,bs, "T-");
				addToMapNoCollision(MyUtil.stringToBit,"T-",bs);
			}
			else if(i==16)
			{
				bs = createBitSetFromString(Integer.toBinaryString(16));
				addToMapNoCollision(MyUtil.bitToString,bs, "-A");
				addToMapNoCollision(MyUtil.stringToBit,"-A",bs);
			}
			else if(i==17)
			{
				bs = createBitSetFromString(Integer.toBinaryString(17));
				addToMapNoCollision(MyUtil.bitToString,bs, "-C");
				addToMapNoCollision(MyUtil.stringToBit,"-C",bs);
			}
			else if(i==18)
			{
				bs = createBitSetFromString(Integer.toBinaryString(18));
				addToMapNoCollision(MyUtil.bitToString,bs, "-G");
				addToMapNoCollision(MyUtil.stringToBit,"-G",bs);
			}
			else if(i==19)
			{
				bs = createBitSetFromString(Integer.toBinaryString(19));
				addToMapNoCollision(MyUtil.bitToString,bs, "-T");
				addToMapNoCollision(MyUtil.stringToBit,"-T",bs);
			}
		}
	}
	
	public static void selectKmersWithEntropy(double entropyValue, Entropy<?> entropy,boolean jelly,String fileNameKmer,int leftborder, int rightborder,KmerSet_binary ks)
	{
		BufferedReader in=null;
		try
		{
			in = new BufferedReader(new FileReader(fileNameKmer));
			String kmer;
			if(!jelly)
			{
				while(in.ready())
				{
					kmer=in.readLine();
					if(kmer.indexOf("N")==-1 && Integer.valueOf(kmer.split("\t")[0])<=rightborder &&Integer.valueOf(kmer.split("\t")[0])>=leftborder && entropy.calcEntropy(kmer.split("\t")[1])>=entropyValue)
					{
						if(ks !=null)
						{
							 ks.addKmer(kmerFunctions.kmerToUse(kmer.split("\t")[1]),Integer.valueOf(kmer.split("\t")[0])); //already checked for revComp....
						}
						else
						{
							ks= new KmerSet_binary(kmer.split("\t")[1].length());
							ks.addKmer(kmerFunctions.kmerToUse(kmer.split("\t")[1]),Integer.valueOf(kmer.split("\t")[0]));
						}
					}
				}
			}
			else
			{
				while(in.ready())
				{
					kmer=in.readLine();
					//System.out.println(kmer);
					if(kmer.indexOf("N")==-1 && Integer.valueOf(kmer.split("\t")[1])<=rightborder &&Integer.valueOf(kmer.split("\t")[1])>=leftborder&&entropy.calcEntropy(kmer.split("\t")[0])>=entropyValue)
					{
						if(ks !=null)
						{
							 ks.addKmer(kmerFunctions.kmerToUse(kmer.split("\t")[0]),Integer.valueOf(kmer.split("\t")[1])); //already checked for revComp....
						}
						else
						{
							ks= new KmerSet_binary(kmer.split("\t")[0].length());
							ks.addKmer(kmerFunctions.kmerToUse(kmer.split("\t")[0]),Integer.valueOf(kmer.split("\t")[1]));
						}
					}
				}
			}

		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public static void calculateEntropyOfSampleKmer(Entropy<?> entropy,boolean jelly,String fileNameKmer,int leftborder, int rightborder,String filenameOutput)
	{
		BufferedReader in=null;
		PrintWriter pw = null;
		try
		{
			pw = new PrintWriter(new BufferedWriter(new FileWriter(filenameOutput)));
			in = new BufferedReader(new FileReader(fileNameKmer));
			String kmer;
			if(!jelly)
			{
				while(in.ready())
				{
					kmer=in.readLine();
					if(kmer.indexOf("N")==-1 && Integer.valueOf(kmer.split("\t")[0])<=rightborder &&Integer.valueOf(kmer.split("\t")[0])>=leftborder)
					{
						pw.append(String.valueOf(entropy.calcEntropy(kmer.split("\t")[1]))+"\n");
					}
				}
			}
			else
			{
				while(in.ready())
				{
					kmer=in.readLine();
					//System.out.println(kmer);
					if(kmer.indexOf("N")==-1 && Integer.valueOf(kmer.split("\t")[1])<=rightborder &&Integer.valueOf(kmer.split("\t")[1])>=leftborder)
					{
						pw.append(String.valueOf(entropy.calcEntropy(kmer.split("\t")[0]))+"\n");
					}
				}
			}
			pw.flush();
			pw.close();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public static void selectKmerWithEntropy(Entropy<?> entropy,boolean jelly,String fileNameKmer,int leftborder, int rightborder,double entropyValue,String filenameOutput)
	{
		BufferedReader in=null;
		PrintWriter pw = null;
		try
		{
			pw = new PrintWriter(new BufferedWriter(new FileWriter(filenameOutput)));
			in = new BufferedReader(new FileReader(fileNameKmer));
			String kmer;
			if(!jelly)
			{
				while(in.ready())
				{
					kmer=in.readLine();
					if(kmer.indexOf("N")==-1 && Integer.valueOf(kmer.split("\t")[0])<=rightborder &&Integer.valueOf(kmer.split("\t")[0])>=leftborder &&entropy.calcEntropy(kmer.split("\t")[1])>=entropyValue)
					{
						pw.append(kmer+"\n");
					}
				}
			}
			else
			{
				while(in.ready())
				{
					kmer=in.readLine();
					//System.out.println(kmer);
					if(kmer.indexOf("N")==-1 && Integer.valueOf(kmer.split("\t")[1])<=rightborder &&Integer.valueOf(kmer.split("\t")[1])>=leftborder && entropy.calcEntropy(kmer.split("\t")[0])>=entropyValue)
					{
						pw.append(kmer+"\n");
					}
				}
			}
			pw.flush();
			pw.close();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	
	public static void selectKmerWithEntropyToFastq(Entropy<?> entropy,boolean jelly,String fileNameKmer,int leftborder, int rightborder,double entropyValue,String filenameOutput)
	{
		BufferedReader in=null;
		PrintWriter pw = null;
		try
		{
			pw = new PrintWriter(new BufferedWriter(new FileWriter(filenameOutput)));
			in = new BufferedReader(new FileReader(fileNameKmer));
			String kmer;
			
			while(in.ready())
			{
				kmer=in.readLine();
				if(kmer.indexOf("N")!=-1)
				{
					continue;
				}
				String kmer_val="";
				int count=0;
				String[]temp =kmer.split("\t");
				if(!jelly)
				{
					kmer_val=temp[1];
					count=Integer.valueOf(temp[0]);
				}
				else
				{
					kmer_val=temp[0];
					count=Integer.valueOf(temp[1]);
				}
				
				if(count<=rightborder && count>=leftborder)
				{
					double val =entropy.calcEntropy(kmer_val); 
					if(val<entropyValue && val >=1.5)
					{
						StringBuilder sb = new StringBuilder();
						sb.append(count);
						sb.append("_");
						sb.append(kmer_val);
						sb.append("\n");
						pw.append(sb.toString());
						sb.delete(0, sb.length());
						sb.append(kmer_val);
						sb.append("\n");
						pw.append(sb.toString());
						sb.delete(0, sb.length());
						sb.append("+\n");
						for(int i=0; i < kmer_val.length();++i)
						{
							sb.append("%");
						}
						sb.append("\n");
						pw.append(sb.toString());
						sb.delete(0, sb.length());
					}
				}
			}
			pw.flush();
			pw.close();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	
		
	public static KmerSet_binary readKmers(String fileNameKmer, boolean jelly, int leftborder, int rightborder, KmerSet_binary ks)
	{
		BufferedReader in;
		try 
		{
//			FileHandler fh = new FileHandler("log");
//			log.addHandler(fh);
			in = new BufferedReader(new FileReader(fileNameKmer));
			String kmer;
			//System.out.println("reading kmers");
			if(!jelly)
			{
				while(in.ready())
				{
					kmer=in.readLine();
					if(kmer.indexOf("N")==-1 && Integer.valueOf(kmer.split("\t")[0])<=rightborder &&Integer.valueOf(kmer.split("\t")[0])>=leftborder)
					{
						if(ks !=null)
						{
							 ks.addKmer(kmerFunctions.kmerToUse(kmer.split("\t")[1]),Integer.valueOf(kmer.split("\t")[0])); //already checked for revComp....
						}
						else
						{
							ks= new KmerSet_binary(kmer.split("\t")[1].length());
							ks.addKmer(kmerFunctions.kmerToUse(kmer.split("\t")[1]),Integer.valueOf(kmer.split("\t")[0]));
						}
					}
				}
			}
			else
			{
				while(in.ready())
				{
					kmer=in.readLine();
					//System.out.println(kmer);
					if(kmer.indexOf("N")==-1 && Integer.valueOf(kmer.split("\t")[1])<=rightborder &&Integer.valueOf(kmer.split("\t")[1])>=leftborder)
					{
						if(ks !=null)
						{
							 ks.addKmer(kmerFunctions.kmerToUse(kmer.split("\t")[0]),Integer.valueOf(kmer.split("\t")[1])); //already checked for revComp....
						}
						else
						{
							ks= new KmerSet_binary(kmer.split("\t")[0].length());
							ks.addKmer(kmerFunctions.kmerToUse(kmer.split("\t")[0]),Integer.valueOf(kmer.split("\t")[1]));
						}
					}
				}
			}
			in.close();
			return ks;
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return null;
	}
	
	
	
	
	public static int callProcess(String command) throws Exception
	{
		String delim="[ ]";
		
		String[] token = command.split(delim); 
		//System.out.println(command);
		List<String> command_arg = new ArrayList<>();
		for(int i=0; i < token.length;++i)
		{
			//System.out.println(token[i]);
			command_arg.add(token[i]);
		}
		//System.out.println();
		ProcessBuilder builder = new ProcessBuilder(command_arg);
		Process called = builder.start();
		int exitval = called.waitFor();
		//System.out.println(exitval);
		return exitval;
	}
	
	public static Process callProcessAndGetProcess(String command) throws Exception
	{
		String delim="[ ]";
		
		String[] token = command.split(delim); 
		//System.out.println(command);
		List<String> command_arg = new ArrayList<>();
		for(int i=0; i < token.length;++i)
		{
			//System.out.println(token[i]);
			command_arg.add(token[i]);
		}
		//System.out.println();
		ProcessBuilder builder = new ProcessBuilder(command_arg);
		builder.redirectErrorStream(true);
		Process called = builder.start();
		
		return called;
	}
	
	
}
